package com.zwl.util;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;
import lombok.extern.apachecommons.CommonsLog;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * @author zhangweilin
 * @date 2018/7/18 10:03
 * @description: 反向工具类
 */
@CommonsLog
public class ReflectUtil {

    /**
     * 用于对类的字段赋值，无视private,project修饰符,无视set/get方法
     *
     * @param object 要反射的类
     * @param args   类的字段名和值 每个字段名和值用英文逗号隔开
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Object setPrivateField(Object object, String... args) {
        try {
            Class<?> obj = object.getClass();
            Field[] fields = obj.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true);
                for (int j = 0; j < args.length; j++) {
                    String str = args[j];
                    String strs[] = str.split(",");
                    if (strs[0].equals(fields[i].getName())) {
                        fields[i].set(object, strs[1]);
                        break;
                    }
                }
            }
            return object;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void makeFieldCanAccess(Field field) throws NoSuchFieldException, IllegalAccessException {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        if (Modifier.isFinal(field.getModifiers())) {
            // System.out.println("是final11");

            modifiersField.set(field, field.getModifiers() & ~Modifier.FINAL);
            // modifiersField.set(field, field.getModifiers() &
            // ~Modifier.STATIC); //静态无须考虑，静态不属于实例
        }
    }



    /**
     * 读取常量值
     *
     * @param clazz
     * @param clazz2
     * @param propertyName
     * @return
     */
    public static <T> T readConstantProperty(Class<?> clazz, String propertyName) {
        try {
            Field field = clazz.getDeclaredField(propertyName);
            makeFieldCanAccess(field);
            return (T) field.get(null);
        } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取一个get方法
     *
     * @param objectClass
     * @param fieldName
     * @return
     */
    public static Method getGetMethod(Class<?> objectClass, String fieldName) {
        StringBuffer sb = new StringBuffer();
        sb.append("get");
        sb.append(fieldName.substring(0, 1).toUpperCase());
        sb.append(fieldName.substring(1));
        try {
            return objectClass.getMethod(sb.toString());
        } catch (Exception e) {
        }
        return null;
    }




    /**
     * 创建属性
     *
     * @param modifiers
     * @param typeClassName
     * @param fieldName
     * @param classPool
     * @param ctClass
     * @throws CannotCompileException
     * @throws NotFoundException
     */
    private static void addField(int modifiers, String typeClassName, String fieldName, ClassPool classPool, CtClass ctClass) throws CannotCompileException, NotFoundException {
        CtField ctField = new CtField(classPool.get(typeClassName), fieldName, ctClass);
        //指定该字段 修饰
        ctField.setModifiers(modifiers);
        ctClass.addField(ctField);
    }



    /**
     * 使用反射设置变量值
     *
     * @param target    被调用对象
     * @param fieldName 被调用对象的字段，一般是成员变量或静态变量，不可是常量！
     * @param value     值
     * @param <T>       value类型，泛型
     */
    public static <T> void setFieldValue(Object target, String fieldName, T value) {
        try {

            Field f = getField(target, fieldName);
            f.setAccessible(true);
            f.set(target, value);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            throw new RuntimeException("给field设置值失败", e);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("给field设置值失败", e);
        }
    }
    /**
     * 使用反射设置变量值,如果为null的话
     *
     * @param target    被调用对象
     * @param fieldName 被调用对象的字段，一般是成员变量或静态变量，不可是常量！
     * @param value     值
     * @param <T>       value类型，泛型
     */
    public static <T> void setFieldValueIfNull(Object target, String fieldName, T value) {
        try {
            Field f = getField(target, fieldName);
            if (f == null) {
                log.warn("实例【"+target+"】找不到属性【"+fieldName+"】,忽略");
                return;
            }
            f.setAccessible(true);
            Object value0 = (T) f.get(target);
            if (null==value0) {
                f.set(target, value);
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            throw new RuntimeException("给field设置值失败", e);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("给field设置值失败", e);
        }
    }

    public static Field getField(Object target, String fieldName) throws NoSuchFieldException {
        Class<?> c = target.getClass();
        while (Object.class != c && c != null){
            try {
                Field field = c.getDeclaredField(fieldName);
                return field;
            } catch (NoSuchFieldException | SecurityException e) {
//                e.printStackTrace();
                log.debug(c.getName()+"中未找到属性"+fieldName+"，尝试到父类中找");
            }
            c = c.getSuperclass();
        }
        return null;
    }

    /**
     * 使用反射设置变量值
     *
     * @param target    被调用对象
     * @param fieldName 被调用对象的字段，一般是成员变量或静态变量，不可是常量！
     * @param value     值
     * @param <T>       value类型，泛型
     */
    public static <T> void setFieldValue(Object target, Field field, T value) {
        try {
            Field f = getField(target, field.getName());
            f.setAccessible(true);
            f.set(target, value);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            throw new RuntimeException("给field设置值失败", e);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("给field设置值失败", e);
        }
    }
    /**
     * 使用反射设置变量值
     *
     * @param target    被调用对象
     * @param fieldName 被调用对象的字段，一般是成员变量或静态变量，不可是常量！
     * @param value     值
     * @param <T>       value类型，泛型
     */
    public static <T> void setFieldValueIfNull(Object target, Field field, T value) {
        try {
            Field f = getField(target, field.getName());
            f.setAccessible(true);
            Object value0 = f.get(target);
            if (null==value0) {
                f.set(target, value);
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            throw new RuntimeException("给field设置值失败", e);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("给field设置值失败", e);
        }
    }



    /**
     * 使用反射获取变量值
     *
     * @param target    被调用对象
     * @param fieldName 被调用对象的字段，一般是成员变量或静态变量，不可以是常量
     * @param <T>       返回类型，泛型
     * @return 值
     */
    public static <T> T getFieldValue(Object target, String fieldName) {
        T value = null;
        try {
            Field f = getField(target, fieldName);
            assert f != null;
            f.setAccessible(true);
            value = (T) f.get(target);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return value;
    }

    public static <T> T getFieldValue(Object target, Field field) {
        T value = null;
        try {
            Class<?> c = target.getClass();
            field.setAccessible(true);
            value = (T) field.get(target);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return value;
    }


}
